<!DOCTYPE html>
<html>

<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
	<meta name="theme-color" content="#33474d">
	<title>失落的乐章</title>
	<link rel="stylesheet" href="/css/style.css" />
	
      <link rel="alternate" href="/atom.xml" title="失落的乐章" type="application/atom+xml">
    
</head>

<body>

	<header class="header">
		<nav class="header__nav">
			
				<a href="/archives" class="header__link">Archive</a>
			
				<a href="/tags" class="header__link">Tags</a>
			
				<a href="/atom.xml" class="header__link">RSS</a>
			
		</nav>
		<h1 class="header__title"><a href="/">失落的乐章</a></h1>
		<h2 class="header__subtitle">技术面前，永远都是学生。</h2>
	</header>

	<main>
		
	<span class="different-posts different-posts_earlier">📖 <a href="/page/31">earlier posts</a> 📖</span>




	<article>
	
		<h1><a href="/2017/10/12/MongoDB/14. 搭建高可用mongodb集群—— 配置mongodb/">搭建高可用mongodb集群—— 配置mongodb</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/MongoDB/">MongoDB</a>
			</span>
		
	</div>

	

	
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在大数据的时代，传统的关系型数据库要能更高的服务必须要解决高并发读写、海量数据高效存储、高可扩展性和高可用性这些难题。不过就是因为这些问题Nosql诞生了。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;NOSQL有这些优势：</p>
<ul>
<li><strong>大数据量</strong>，可以通过廉价服务器存储大量的数据，轻松摆脱传统mysql单表存储量级限制。</li>
<li><strong>高扩展性</strong>，Nosql去掉了关系数据库的关系型特性，很容易横向扩展，摆脱了以往老是纵向扩展的诟病。</li>
<li><strong>高性能</strong>，Nosql通过简单的key-value方式获取数据，非常快速。还有NoSQL的Cache是记录级的，是一种细粒度的Cache，所以NoSQL在这个层面上来说就要性能高很多。</li>
<li><strong>灵活的数据模型</strong>，NoSQL无需事先为要存储的数据建立字段，随时可以存储自定义的数据格式。而在关系数据库里，增删字段是一件非常麻烦的事情。如果是非常大数据量的表，增加字段简直就是一个噩梦。</li>
<li><strong>高可用</strong>，NoSQL在不太影响性能的情况，就可以方便的实现高可用的架构。比如mongodb通过mongos、mongo分片就可以快速配置出高可用配置。</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在nosql数据库里，大部分的查询都是键值对（key、value）的方式。MongoDB是一个介于  关系数据库和非关系数据库之间的产品，是非关系数据库当中最像关系数据库的。支持类似于面向对象的查询语言，几乎可以实现类似关系数据库单表查询的绝大部分功能，而且还支持对数据建立索引。所以这个非常方便，我们可以用sql操作MongoDB，从关系型数据库迁移过来，开发人员学习成本会大大减少。如果再对底层的sql API做一层封装，开发基本可以感觉不到mongodb和关系型数据库的区别。同样MongoDB也是号称自己能够快速搭建一个高可用可扩展的的分布式集群，网上有很多搭建的文章，在我们搭建的时候还需要查找修改很多东西，所以把自己实战的步骤记录下来以备忘。我们看看如何一步一步搭建这个东东。</p>
<h2 id="一、mongodb单实例。"><a href="#一、mongodb单实例。" class="headerlink" title="一、mongodb单实例。"></a>一、mongodb单实例。</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;这种配置只适合简易开发时使用，生产使用不行，因为单节点挂掉整个数据业务全挂，如下图。</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/%E6%90%AD%E5%BB%BA%E9%AB%98%E5%8F%AF%E7%94%A8mongodb%E9%9B%86%E7%BE%A4%E2%80%94%E2%80%94%20%E9%85%8D%E7%BD%AEmongodb/01.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;虽然不能生产使用，但这个模式可以快速搭建启动，并且能够用mongodb的命令操作数据库。下面列出在linux下安装单节点mongodb的步骤</p>
<ol>
<li>建立mongodb测试文件夹</li>
</ol>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="comment">#存放整个mongodb文件</span></div><div class="line">mkdir -p /data/mongodbtest/single </div><div class="line"></div><div class="line"><span class="comment">#存放mongodb数据文件</span></div><div class="line">mkdir -p /data/mongodbtest/single/data</div><div class="line"></div><div class="line"><span class="comment">#进入mongodb文件夹</span></div><div class="line"><span class="built_in">cd</span>  /data/mongodbtest/single</div></pre></td></tr></table></figure>
<ol>
<li>下载mongodb的安装程序包</li>
</ol>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">wget http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.4.6.tgz</div><div class="line"></div><div class="line"><span class="comment">#解压下载的压缩包  </span></div><div class="line">tar xvzf mongodb-linux-x86_64-2.4.6.tgz</div><div class="line"></div><div class="line"><span class="comment">#进入mongodb程序执行文件夹</span></div><div class="line"><span class="built_in">cd</span> mongodb-linux-x86_64-2.4.6/bin/</div></pre></td></tr></table></figure>
<ol>
<li>启动单实例mongodb</li>
</ol>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">mongod  --dbpath /data/mongodbtest/single/data</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;输出日志如下，成功！</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">[initandlisten] db version v2.4.6</div><div class="line">……..</div><div class="line">[initandlisten] waiting <span class="keyword">for</span> connections on port 27017</div><div class="line">[websvr] admin web console waiting <span class="keyword">for</span> connections on port 28017</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;mongodb默认自带提供了web访问接口，通过 IP + 端口的形式可以访问。<a href="http://192.168.0.1:28017/" target="_blank" rel="external">http://192.168.0.1:28017/</a></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/%E6%90%AD%E5%BB%BA%E9%AB%98%E5%8F%AF%E7%94%A8mongodb%E9%9B%86%E7%BE%A4%E2%80%94%E2%80%94%20%E9%85%8D%E7%BD%AEmongodb/02.png?raw=true" alt=""></figure></p>
<h2 id="二、主从模式。"><a href="#二、主从模式。" class="headerlink" title="二、主从模式。"></a>二、主从模式。</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;使用mysql数据库时大家广泛用到，采用双机备份后主节点挂掉了后从节点可以接替主机继续服务。所以这种模式比单节点的高可用性要好很多。</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/%E6%90%AD%E5%BB%BA%E9%AB%98%E5%8F%AF%E7%94%A8mongodb%E9%9B%86%E7%BE%A4%E2%80%94%E2%80%94%20%E9%85%8D%E7%BD%AEmongodb/03.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;下面看一下怎么一步步搭建一个mongodb的主从复制节点：</p>
<ol>
<li><p>准备两台机器 192.168.0.1 和 192.168.0.2。 192.168.0.1 当作主节点， 192.168.0.2作为从节点。</p>
</li>
<li><p>分别下载mongodb安装程序包。在192.168.0.1上建立文件夹 /data/mongodbtest/master，192.168.0.2建立文件夹/data/mongodbtest/slave。</p>
</li>
<li><p>在192.168.0.1启动mongodb主节点程序。注意后面的这个 “ –master ”参数，标示主节点。</p>
</li>
</ol>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">mongod –dbpath /data/mongodbtest/master –master</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;输出日志如下，成功！</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">[initandlisten] MongoDB starting : pid=18285 port=27017 dbpath=/data/mongodbtest/master master=1</div><div class="line"><span class="comment">#日志显示主节点参数</span></div><div class="line">[initandlisten] options: &#123; dbpath: “/data/mongodbtest/master”, master: <span class="literal">true</span> &#125;</div><div class="line">……..</div><div class="line">[initandlisten] waiting <span class="keyword">for</span> connections on port 27017</div></pre></td></tr></table></figure>
<ol>
<li>在192.168.0.2启动mongodb从节点程序。关键配置，指定主节点ip地址和端口 –source 192.168.0.1:27017 和 标示从节点 –source 参数。</li>
</ol>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">mongod –dbpath /data/mongodbtest/slave –slave –<span class="built_in">source</span> 192.168.0.1:27017</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;输出日志如下，成功！</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">[initandlisten] MongoDB starting : pid=17888 port=27017 dbpath=/data/mongodbtest/slave slave=1</div><div class="line">……..</div><div class="line">    <span class="comment">#日志显示从节点参数</span></div><div class="line">[initandlisten] options: &#123; dbpath: “/data/mongodbtest/slave”, slave: <span class="literal">true</span>, <span class="built_in">source</span>: “192.168.0.1:27017″ &#125;</div><div class="line">……..</div><div class="line">[initandlisten] waiting <span class="keyword">for</span> connections on port 27017</div><div class="line">    <span class="comment">#日志显示从节点 从主节点同步复制数据</span></div><div class="line">[replslave] repl: from host:192.168.0.1:27017</div></pre></td></tr></table></figure>
<ol>
<li>测试主从复制。</li>
</ol>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在主节点上连接到终端：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">mongo 127.0.0.1</div><div class="line"></div><div class="line"><span class="comment">#建立test 数据库。</span></div><div class="line">use <span class="built_in">test</span>;</div><div class="line"></div><div class="line">往testdb表插入数据。</div><div class="line">&gt; db.testdb.insert(&#123;<span class="string">"test1"</span>:<span class="string">"testval1"</span>&#125;)</div><div class="line"></div><div class="line">查询testdb数据看看是否成功。</div><div class="line">&gt; db.testdb.find();</div><div class="line">&#123; <span class="string">"_id"</span> : ObjectId(<span class="string">"5284e5cb1f4eb215b2ecc463"</span>), <span class="string">"test1"</span> : <span class="string">"testval1"</span> &#125;</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;可以看到主机的同步日志</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">[initandlisten] connection accepted from 192.168.0.2:37285 <span class="comment">#3 (2 connections now open)</span></div><div class="line">[slaveTracking] update local.slaves query: &#123; _id: ObjectId(’5284e6268ed115d6238bdb39′), config: &#123; host: “192.168.0.2:35271″, upgradeNeeded: <span class="literal">true</span> &#125;, ns: “local.oplog.<span class="variable">$main</span>” &#125; update: &#123; <span class="variable">$set</span>: &#123; syncedTo: Timestamp 1384441570000|1 &#125; &#125; nscanned:1 nupdated:1 fastmod:1 keyUpdates:0 locks(micros) w:132015 132ms</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;检查从主机的数据。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">mongo 127.0.0.1</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;查看当前数据库。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">&gt; show dbs;</div><div class="line">  <span class="built_in">local</span>   0.203125GB</div><div class="line">  <span class="built_in">test</span>    0.203125GB</div><div class="line"></div><div class="line">use <span class="built_in">test</span>;</div><div class="line">db.testdb.find();</div><div class="line">&#123; <span class="string">"_id"</span> : ObjectId(<span class="string">"5284e5cb1f4eb215b2ecc463"</span>), <span class="string">"test1"</span> : <span class="string">"testval1"</span> &#125;</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;查询后数据已经同步过来了。再看看日志，发现从主机确实从主机同步了数据。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">Thu Nov 14 23:05:13 [replslave] repl:   checkpoint applied 15 operations</div><div class="line">Thu Nov 14 23:05:13 [replslave] repl:   syncedTo: Nov 14 23:08:10 5284e75a:1</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;查看服务状态</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">&gt; db.printReplicationInfo();</div><div class="line">          this is a slave, printing slave replication info.</div><div class="line">          <span class="built_in">source</span>:   192.168.0.1:27017</div><div class="line">              syncedTo: Sun Nov 17 2013 16:04:02 GMT+0800 (CST)</div><div class="line">                      = -54 secs ago (-0.01hrs)</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;到此主从结构的mongodb搭建好了。</p>
<h3 id="故障转移测试"><a href="#故障转移测试" class="headerlink" title="故障转移测试"></a>故障转移测试</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;现在两台服务器如果主服务器挂掉了，从服务器可以正常运转吗？</p>
<ul>
<li>a、先测试下从服务器可以当成主服务器吗，也就是往从服务器里写能够同步主服务器吗？</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在192.168.0.2上连接mongodb。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">mongo 127.0.0.1:27017</div><div class="line">&gt; db.testdb.insert(&#123;<span class="string">"test3"</span>:<span class="string">"testval3"</span>&#125;);</div><div class="line">not master</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;可以看到 mongodb的从节点是不能提供写操作的，只能提供读操作。</p>
<ul>
<li>b、如果从服务器挂掉，主服务器还可以提供服务。如果主服务器挂掉了从服务器能否自动变为可写。</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;测试一下！</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;先杀掉原来的mongodb主服务器。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">kill</span> -3 `ps -ef|grep mongod|grep -v grep|awk <span class="string">'&#123;print $2&#125;'</span>`</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;测试从服务器能否可写。在192.168.0.2上连接mongodb测试。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">&gt; db.testdb.insert(&#123;<span class="string">"test3"</span>:<span class="string">"testval3"</span>&#125;);</div><div class="line">not master</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;看起来从服务器没有自动接替主服务器的功能，只有手工处理了！</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;停止从服务器，在原数据文件启动并添加主服务器标示。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">mongod  --dbpath /data/mongodbtest/slave --master</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;等到启动成功（时间有点长）。在192.168.0.2 上 连接</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">mongo 192.168.0.2:27017</div><div class="line"></div><div class="line">&gt; db.testdb.find();</div><div class="line">&#123; <span class="string">"_id"</span> : ObjectId(<span class="string">"5288629e9b0318be4b20bd4c"</span>), <span class="string">"test1"</span> : <span class="string">"testval1"</span> &#125;</div><div class="line">&#123; <span class="string">"_id"</span> : ObjectId(<span class="string">"528862d69b0318be4b20bd4d"</span>), <span class="string">"test2"</span> : <span class="string">"testval2"</span> &#125;</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;成功！</p>
<h3 id="多个从节点"><a href="#多个从节点" class="headerlink" title="多个从节点"></a>多个从节点</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;现在只是一个数据库服务器又提供写又提供读，机器承载会出现瓶颈。大家还记得mysql里的读写分离吗？把20%的写放到主节点，80%的读放到从节点分摊了减少了服务器的负载。但是大部分应用都是读操作带来的压力，一个从节点压力负载不了，可以把一个从节点变成多个节点。那mongodb的一主多从可以支持吗？答案是肯定的。</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/%E6%90%AD%E5%BB%BA%E9%AB%98%E5%8F%AF%E7%94%A8mongodb%E9%9B%86%E7%BE%A4%E2%80%94%E2%80%94%20%E9%85%8D%E7%BD%AEmongodb/04.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;为了方便测试，在192.168.0.2上再建立一个文件夹 /data/mongodbtest/slave1 作为另一个slave服务器。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;启动slave2服务，</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">mongod  --dbpath /data/mongodbtest/slave1 --slave  --port 27017 --<span class="built_in">source</span> 192.168.0.1:27017。</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;成功启动后通过mongodb连接测试：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">&gt; db.testdb.find();</div><div class="line">&#123; <span class="string">"_id"</span> : ObjectId(<span class="string">"5288629e9b0318be4b20bd4c"</span>), <span class="string">"test1"</span> : <span class="string">"testval1"</span> &#125;</div><div class="line">&#123; <span class="string">"_id"</span> : ObjectId(<span class="string">"528862d69b0318be4b20bd4d"</span>), <span class="string">"test2"</span> : <span class="string">"testval2"</span> &#125;</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;搭建了这套主从复制系统是不是就很稳健了，其实不然。。。看看这几个问题？</p>
<ul>
<li>主节点挂了能否自动切换连接？目前需要手工切换。</li>
<li>主节点的写压力过大如何解决？</li>
<li>从节点每个上面的数据都是对数据库全量拷贝，从节点压力会不会过大？</li>
<li>就算对从节点路由实施路由访问策略能否做到自动扩展？</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;参考：<br>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<a href="http://www.infoq.com/cn/news/2011/01/nosql-why/" target="_blank" rel="external">NoSQL开篇——为什么要使用NoSQL</a></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<a href="http://cn.docs.mongodb.org/manual/single/" target="_blank" rel="external">mongodb手册</a></p>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/MongoDB/13. MongoDB“关于ReplSet成员必须为奇数”的说法，其实成员节点数目可以为偶数的/">MongoDB“关于ReplSet成员必须为奇数”的说法，其实成员节点数目可以为偶数的</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/MongoDB/">MongoDB</a>
			</span>
		
	</div>

	

	
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;关于ReplSet成员必须为奇数的情况说明。<br>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;ReplSet优势：Replica Set是增加冗余及提升MongoDB数据集群性能的有效途径。数据在所有的节点中被复制，并选出一个作为主节点。假如主节点出故障，那么会在其他的节点中票选一个作为新的主节点。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;问题：在MongoDB中同一个RepliSet中的成员数量不能是偶数个。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;假如你使用了偶数个成员，那么当主节点发生故障时那么其它的节点都会变成只读。此时呢，无法选出PRIMARY节点。发生这种情况是因为剩下待选节点的数目不满足票选主节点的规定。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;结论：目前的MongoDB版本中，使用ReplSet机制成员数量必须控制在奇数数目个。</p>
<hr>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;以上是网上的结论，实际做测试发现网上的说法有误！</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;此文章中第二页有提到这个观点：<a href="https://hcldirgit.github.io/2017/08/27/MongoDB/11.%20MongoDB%20Master%EF%BC%9AMongoDB%E2%80%9C%E8%AF%9F%E7%97%85%E2%80%9D%E5%88%86%E6%9E%90/" target="_blank" rel="external">MongoDB Master：MongoDB“诟病”分析</a></p>
<hr>
<hr>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;测试结论：当ReplSet成员是2个时，PRIMARY挂了，的确SECONDARY无法升级为PRIMARY；但是，当ReplSet成员是4个时，PRIMARY挂了，SECONDARY的确是可以升级为PRIMARY的。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;当然，测试只是针对ReplSet成员为偶数情况；奇数情况是没有问题的。</p>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/MongoDB/12. MongoDB副本集学习：性能和优化相关/">MongoDB副本集学习：性能和优化相关</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/MongoDB/">MongoDB</a>
			</span>
		
	</div>

	

	
		<h2 id="Read-Preferences-读写分离"><a href="#Read-Preferences-读写分离" class="headerlink" title="Read Preferences/读写分离"></a>Read Preferences/读写分离</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;有时候为了考虑应用程序的性能或响应性，为了提高读取操作的吞吐率，一个常见的措施就是进行读写分离，MongoDB副本集对读写分离的支持是通过Read Preferences特性进行支持的，这个特性非常复杂和灵活。以下几种应用场景可能会考虑对副本集进行读写分离：</p>
<ol>
<li>操作不影响前端应用程序，比如备份或者报表；</li>
<li>在一个物理上分布的副本集群中，为了减少应用程序的延迟，可能会优先选择离应用程序更近的secondary节点而不是远在千里之外机房的主节点；</li>
<li>故障发生时候能够提供一个优雅的降级。副本集primary节点宕机后再选出新的primary节点这段时间内（10秒或更长时间）能够依然响应客户端应用的读请求；</li>
<li>应用能够容忍一定程度的数据不一致性。</li>
</ol>
<h3 id="Read-References"><a href="#Read-References" class="headerlink" title="Read References:"></a>Read References:</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;应用程序驱动通过read reference来设定如何对副本集进行读取操作，默认的,客户端驱动所有的读操作都是直接访问primary节点的，从而保证了数据的严格一致性。</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/MongoDB%E5%89%AF%E6%9C%AC%E9%9B%86%E5%AD%A6%E4%B9%A0%EF%BC%9A%E6%80%A7%E8%83%BD%E5%92%8C%E4%BC%98%E5%8C%96%E7%9B%B8%E5%85%B3/01.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;但有时为了缓解主节点的压力，可能需要直接从secondary节点读取，只需要保证最终一致性就可以了。</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/MongoDB%E5%89%AF%E6%9C%AC%E9%9B%86%E5%AD%A6%E4%B9%A0%EF%BC%9A%E6%80%A7%E8%83%BD%E5%92%8C%E4%BC%98%E5%8C%96%E7%9B%B8%E5%85%B3/02.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;MongoDB 2.0之后支持五种的read preference模式：</p>
<ul>
<li><p><strong>primary</strong>:默认，只从主节点上进行读取操作；</p>
</li>
<li><p><strong>primaryPreferred</strong>:在绝大部分的情形都是从主节点上读取数据的,只有当主节点不可用的时候，比如在进行failover的10秒或更长的时间内会从secondary节点读取数据。</p>
</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>警告</strong>：2.2版本之前的MongoDB对Read Preference支持的还不完全，如果客户端驱动采用primaryPreferred实际上读取操作都会被路由到secondary节点。</p>
<ul>
<li><p><strong>secondary</strong>:只从secondary节点上进行读取操作，存在的问题是secondary节点的数据会比primary节点数据“旧”。</p>
</li>
<li><p><strong>secondaryPreferred</strong>:优先从secondary节点进行读取操作；</p>
</li>
<li><p><strong>nearest</strong>:既有可能从primary，也有可能从secondary节点读取，这个决策是通过一个叫member selection过程处理的。</p>
</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;MongoDB允许在不同的粒度上指定这些模式:连接、数据库、集合甚至单次的操作。不同语言的驱动基本都支持这些粒度。</p>
<h2 id="OpLog"><a href="#OpLog" class="headerlink" title="OpLog"></a>OpLog</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;oplog是一种特殊的capped collection,用来滚动的保存MongoDB中所有数据操作的日志。副本集中secondary节点异步的从primary节点同步oplog然后重新执行它记录的操作，以此达到了数据同步的作用。这就要求oplog必须是幂等的，也就是重复执行相同的oplog记录得到的数据结构必须是相同的。</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/MongoDB%E5%89%AF%E6%9C%AC%E9%9B%86%E5%AD%A6%E4%B9%A0%EF%BC%9A%E6%80%A7%E8%83%BD%E5%92%8C%E4%BC%98%E5%8C%96%E7%9B%B8%E5%85%B3/03.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;事实上副本集中所有节点之间都相互进行heartbeat来维持联系，任何节点都能从其它节点复制oplog。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;capped collection是MongoDB中一种提供高性能插入、读取和删除操作的固定大小集合。当集合被填满的时候，新的插入的文档会覆盖老的文档。因为oplog是capped collection所以指定它的大小非常重要。如果太小那么老的文档很快就被覆盖了，那么宕机的节点就很容易出现无法同步数据的结果，但也不是越大越好，MongoDB在初始化副本集的时候都会有一个默认的oplog大小:</p>
<ul>
<li>在64位的Linux,Solaris,FreeBSD以及Windows系统上，MongoDB会分配磁盘剩余空间的5%作为oplog的大小，如果这部分小于1GB则分配1GB的空间。</li>
<li>在64的OS X系统上会分配183MB。</li>
<li>在32位的系统上则只分配48MB。<br>首先生产环境使用MongoDB毫无疑问必须的是64为操作系统。其次大多数情况下默认的大小是比较适合的。举个例子，如果oplog大小为空闲磁盘的5%，它在24H内能被填满，也就是说secondary节点可以停止复制oplog达24H后仍然能够catch up上primary节点。而且通常的MongoDB副本集的操作量要比这低得多。</li>
</ul>
<h3 id="oplog数据结构："><a href="#oplog数据结构：" class="headerlink" title="oplog数据结构："></a>oplog数据结构：</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;oplog的数据结构如下所示：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">&#123; ts : ..., op: ..., ns: ..., o: ... o2: ...  &#125;</div></pre></td></tr></table></figure>
<ul>
<li>ts: 8字节的时间戳，由4字节unix timestamp + 4字节自增计数表示。这个值很重要，在选举(如master宕机时)新primary时，会选择ts最大的那个secondary作为新primary。</li>
<li>op：1字节的操作类型，例如i表示insert，d表示delete。</li>
<li>ns：操作所在的namespace。</li>
<li>o：操作所对应的document,即当前操作的内容（比如更新操作时要更新的的字段和值）</li>
<li>o2: 在执行更新操作时的where条件，仅限于update时才有该属性</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;其中op有以下几个值：</p>
<ul>
<li>“i”： insert</li>
<li>“u”： update</li>
<li>“d”： delete</li>
<li>“c”： db cmd</li>
<li>“db”：声明当前数据库 (其中ns 被设置成为=&gt;数据库名称+ ‘.’)</li>
<li>“n”:  no op,即空操作，其会定期执行以确保时效性</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>注</strong>：关于oplog有两个常见的错误timestamp error和duplicate error，参考：<a href="http://docs.mongodb.org/manual/tutorial/troubleshoot-replica-sets/#replica-set-troubleshooting-check-oplog-size" target="_blank" rel="external">http://docs.mongodb.org/manual/tutorial/troubleshoot-replica-sets/#replica-set-troubleshooting-check-oplog-size</a></p>
<h3 id="查看oplog大小："><a href="#查看oplog大小：" class="headerlink" title="查看oplog大小："></a>查看oplog大小：</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;通过db.printReplicationInfo() 可以查看副本集节点的oplog状态：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">rs0:PRIMARY&gt; db.printReplicationInfo() </div><div class="line">configured oplog size:   1793.209765625MB</div><div class="line"><span class="built_in">log</span> length start to end: 12.643999999854714secs (0hrs)</div><div class="line">oplog first event time:  Sat Jan 17 1970 06:22:38 GMT+0800 (CST)</div><div class="line">oplog last event time:   Sat Jan 17 1970 06:22:51 GMT+0800 (CST)</div><div class="line">now:                     Sat Aug 17 2013 18:02:12 GMT+0800 (CST)</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;以之前搭建的副本集为例，oplog的大小是1793MB，其中持有的数据时间区间只有12秒。</p>
<h3 id="修改oplog大小："><a href="#修改oplog大小：" class="headerlink" title="修改oplog大小："></a>修改oplog大小：</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;可以在启动mongod的时候指定–oplogSize,单位MB：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">./bin/mongod --fork --dbpath data/rs0-0/ --logpath <span class="built_in">log</span>/rs0-0/rs0-0.log --rest --replSet rs0 --oplogSize 500 --port 37017</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;但有的时候可能需要修改现有副本集的oplog大小。这个非常不推荐，官网有详细的教程，可以参考：<a href="http://docs.mongodb.org/manual/tutorial/change-oplog-size/。" target="_blank" rel="external">http://docs.mongodb.org/manual/tutorial/change-oplog-size/。</a></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在现有的副本集中修改oplog的大小是相当麻烦的而且影响副本集性能，因此最好是预先根据应用的情况评估好oplog的大小：如果应用程序是读多写少，那么默认的大小已经足够了。如果你的应用下面几种场景很多可能考虑需要更大的oplog:</p>
<ul>
<li><strong>在同一个时刻更新多个文档</strong>：oplog为了维持幂等性必须将mutil-updates翻译成一个个独立的操作，这会用去大量的oplog空间，但数据库中的数据量却没有相对称的增加。</li>
<li>多文档同时更新从1.1.3就有的特性，在mongo shell执行类似如下的命令，第四个参数必须制定为true：</li>
<li>db.test.update({foo: “bar”}, {$set: {test: “success!”}}, false, true);</li>
<li><strong>在插入时同时删除相同大小数据</strong>：和上面的结果一样在数据量没有增加的情况下却消耗了大量的oplog空间。</li>
<li><strong>大量的In-Place更新操作</strong>：In-Place更新是指更新文档中原有的部分，但并不增加文档的大小。</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;上面三点总结起来就是<strong>消耗了大量的oplog但是数据量却没有等量的增加</strong>。</p>
<h2 id="数据同步"><a href="#数据同步" class="headerlink" title="数据同步"></a>数据同步</h2><h3 id="数据滞后"><a href="#数据滞后" class="headerlink" title="数据滞后:"></a>数据滞后:</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;前面已经提到MongoDB副本集中secondary节点是通过oplog来同步primary节点数据的，那具体的细节是怎么样的？在说数据如何同步之间先介绍一下replication lag，因为存在数据同步那必然存在一定程度的落后。这个问题对于整个MongoDB副本集的部署是至关重要的。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">rs0:PRIMARY&gt; db.printSlaveReplicationInfo()</div><div class="line"><span class="built_in">source</span>:   192.168.129.129:37019</div><div class="line">    syncedTo: Thu Aug 15 2013 20:59:45 GMT+0800 (CST)</div><div class="line">        = 172971 secs ago (48.05hrs)</div><div class="line"><span class="built_in">source</span>:   192.168.129.129:37020</div><div class="line">    syncedTo: Thu Jan 01 1970 08:00:00 GMT+0800 (CST)</div><div class="line">        = 1376744556 secs ago (382429.04hrs)</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;当前集群的状况是，37017端口是primary节点，37019和37020是secondary节点，其中37020已经宕机，可以看到37019同步数据是在两天前（因为这两天我没有对副本集有任何数据操作），而宕机的节点显示的同步时间是一个很早时间点。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;现在重新启动37020后再执行命令：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">rs0:PRIMARY&gt; db.printSlaveReplicationInfo()</div><div class="line"><span class="built_in">source</span>:   192.168.129.129:37019</div><div class="line">     syncedTo: Thu Aug 15 2013 20:59:45 GMT+0800 (CST)</div><div class="line">         = 175566 secs ago (48.77hrs)</div><div class="line"><span class="built_in">source</span>:   192.168.129.129:37020</div><div class="line">     syncedTo: Thu Aug 15 2013 20:59:45 GMT+0800 (CST)</div><div class="line">         = 175566 secs ago (48.77hrs)</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;可以看到两个secondary节点的同步时间是一致的，向集群中插入几条数据后再执行db.printSlaveReplicationInfo()：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">rs0:PRIMARY&gt; db.test.insert(&#123;<span class="string">"name"</span>:<span class="string">"zhanjindong"</span>,<span class="string">"age"</span>:23&#125;)</div><div class="line">rs0:PRIMARY&gt; db.printSlaveReplicationInfo()</div><div class="line"><span class="built_in">source</span>:   192.168.129.129:37019</div><div class="line">     syncedTo: Sat Aug 17 2013 21:48:31 GMT+0800 (CST)</div><div class="line">         = 6 secs ago (0hrs)</div><div class="line"><span class="built_in">source</span>:   192.168.129.129:37020</div><div class="line">     syncedTo: Sat Aug 17 2013 21:48:31 GMT+0800 (CST)</div><div class="line">         = 6 secs ago (0hrs)</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;可以看到很快就引发了primary和secondary之间的数据同步操作。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;“滞后”是不可避免的，需要做的就是尽可能减小这种滞后，主要涉及到以下几点：</p>
<ul>
<li><strong>网络延迟</strong>：这是所有分布式系统都存在的问题。我们能做的就是尽可能减小副本集节点之间的网络延迟。</li>
<li><strong>磁盘吞吐量</strong>：secondary节点上数据刷入磁盘的速度比primary节点上慢的话会导致secondary节点很难跟上primary节点的节奏。</li>
<li><strong>并发</strong>：并发大的情况下，primary节点上的一些耗时操作会阻塞secondary节点的复制操作，导致复制操作跟不上主节点的写入负荷。解决方法是通过设置操作的write concern（参看这里：<a href="http://docs.mongodb.org/manual/core/write-concern/#replica-set-write-concern）" target="_blank" rel="external">http://docs.mongodb.org/manual/core/write-concern/#replica-set-write-concern）</a>  默认的副本集中写入操作只关心primary节点，但是可以指定写入操作同时传播到其他secondary节点，代价就是严重影响集群的并发性。<pre><code>- **注意**：而且这里还存在一个问题如果，如果写入操作关心的某个节点宕机了，那么操作将会一直被阻塞直到节点恢复。
</code></pre></li>
<li><strong>适当的write concern</strong>：我们为了提高集群写操作的吞吐量经常会将writer concern设置为unacknowledged write concern，这导致primary节点的写操作很快而secondary节点复制操作跟不上。解决方法和第三点是类似的就是在性能和一致性之间做权衡。</li>
</ul>
<h3 id="数据同步-1"><a href="#数据同步-1" class="headerlink" title="数据同步:"></a>数据同步:</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;副本集中数据同步有两个阶段。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>初始化</strong>(initial sync):这个过程发生在当副本集中创建一个新的数据库或其中某个节点刚从宕机中恢复,或者向副本集中添加新的成员的时候,默认的,副本集中的节点会从离它最近的节点复制oplog来同步数据,这个最近的节点可以是primary也可以是拥有最新oplog副本的secondary节点。这可以防止两个secondary节点之间相互进行同步操作。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>复制</strong>(replication):在初始化后这个操作会一直持续的进行着,以保持各个secondary节点之间的数据同步。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在MongoDB2.0以后的版本中,一旦初始化中确定了一个同步的目标节点后，只有当和同步节点之间的连接断开或连接过程中产生异常才可能会导致同步目标的变动，并且具有<strong>就近原则</strong>。考虑两种场景：</p>
<ol>
<li>有两个secondary节点在一个机房，primary在另外一个机房。假设几乎在同一时间启动这三个实例(之前都没有数据和oplog)，那么两个secondary节点应该都是从primary节点同步数据，因为他们之前见都不会拥有比对方更新的oplog。如果重启其中一个secondary，那么它的同步目标将会变成另一个secondary，因为就近原则。</li>
<li>如果有一个primary和一个secondary分别在不同的机房，那么在之前secondary所在的机房中向副本集中新加一个节点时，那么新节点必然是从原先的那个secondary节点同步数据的。</li>
</ol>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在2.2版本以后，数据同步增加了一些额外的行为：</p>
<ol>
<li>secondary节点只有当集群中没有其他选择的时候才会从delayed节点同步数据；</li>
<li>secondary节点绝不会从hidden节点同步数据；</li>
<li>当一个节点新加入副本集中会有一个recovering过程，在这段时间内secondary不会进行数据同步操作；</li>
<li>当一个节点从另一个节点同步数据的时候，需要保证两个节点的local.system.replset.members[n].buildIndexes值是一样的，要不都是false，要不都是true。</li>
</ol>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>注</strong>：buildIndexes指定副本集中成员是否可以创建索引(某些情况下比如没有读操作或者为了提高写性能可以省略索引的创建)。当然即使该值为false，MongoDB还是可以在_id上创建索引以为复制操作服务。</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/MongoDB%E5%89%AF%E6%9C%AC%E9%9B%86%E5%AD%A6%E4%B9%A0%EF%BC%9A%E6%80%A7%E8%83%BD%E5%92%8C%E4%BC%98%E5%8C%96%E7%9B%B8%E5%85%B3/04.png?raw=true" alt=""></figure></p>
<h3 id="重新数据同步："><a href="#重新数据同步：" class="headerlink" title="重新数据同步："></a>重新数据同步：</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;有时当secondary节点落后太多无法追赶上primary节点的时候，这时候可能需要考虑重新同步数据（Resync data）。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;有两种方法一种是指定一个空的目录重新启动落后的节点，这很简单，但是数据量大的情况下回花费很长的时间。另一种方法是基于另一个节点的数据作为“种子”进行重新同步，关于这两种方法在后面向一个现有副本集中添加成员一节会有详细说明。</p>
<h2 id="Elction"><a href="#Elction" class="headerlink" title="Elction"></a>Elction</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在以下几种情景发生的时候，副本集通过“选举”来决定副本集中的primary节点：</p>
<ul>
<li>当第一次初始化一个副本集的时候；</li>
<li>primary几点steps down的时候，出现这种情况可能是因为执行了replSetStepDown命令,或者是集群中出现了更适合当primary的节点，比如当primary节点和集群中其他大部分节点无法通信的时候，当primary steps down的时候,它会关闭所有客户端的连接。</li>
<li>当集群中一个secondary节点无法和primary节点建立连接的时候也会导致一次election发生。</li>
<li>一次failover。</li>
<li>执行rs.conf()命令。</li>
</ul>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/MongoDB%E5%89%AF%E6%9C%AC%E9%9B%86%E5%AD%A6%E4%B9%A0%EF%BC%9A%E6%80%A7%E8%83%BD%E5%92%8C%E4%BC%98%E5%8C%96%E7%9B%B8%E5%85%B3/05.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在一次选举中包括hidden节点、仲裁者甚至正处于recovering状态的节点都具有“投票权”。默认配置中所有参与选举的节点具有相等的权利,当然在一些特定情况下，应明确的指定某些secondary会优先成为primary，比如一个远在千里之外异地机房的节点就不应该成为primary节点，选举的权重通过设置priority来调节,默认该值都是1。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;集群中任何一个节点都可以否决选举,即使它是non-voting member：</p>
<ul>
<li>如果发起选举的节点不具有选举权（priority为0的成员）；</li>
<li>发起选举的节点数据落后太多；</li>
<li>发起选举的节点的priority值比集群中其他某一个节点的小；</li>
<li>如果当前的primary节点比发起选举的节点拥有更新或同等新的数据（也就“optime”值相等或更大）。</li>
<li>当前的primary节点会否决，如果它拥有比发起选举的节点更新或相同新的数据。</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;首先获取最多选票的成员(实际上要超过半数)才会成为primary节点，这也说明了为什么当有两个节点的集群中primary节点宕机后，剩下的只能成为secondary，当primary宕掉,此时副本集只剩下一个secondary,它只有1票,不超过总节点数的半数,它不会选举自己为primary。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;要想更详细的了解选举细节，参考这篇源码分析的文章：<a href="http://nosql-db.com/topic/514e6d9505c3fa4d47017da6" target="_blank" rel="external">http://nosql-db.com/topic/514e6d9505c3fa4d47017da6</a></p>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/MongoDB/11. MongoDB Master：MongoDB“诟病”分析/">MongoDB Master：MongoDB“诟病”分析</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/MongoDB/">MongoDB</a>
			</span>
		
	</div>

	

	
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;近期MongoDB在Hack News上是频繁中枪。许多人更是声称恨上了MongoDB，David mytton就在他的博客中揭露了MongoDB许多现存问题。然而恨的人有之偏爱的也同样很多，作为回击：Russell Smith带来了多年工作经验的总结。Russell Smith曾担任Ops和大型网站缩放顾问并且帮助过Guardian、Experian等多家公司，MongoDB London User Group的联合创始人。作为MongoDB Master(MongoDB官方认可的MongoDB核心贡献者组织，并通过社区分享自己的专业技术)，其参与工作的基础设施单服务器每秒查询超过3万次，每天活跃数据更在1TB以上。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;下面来看Russell对MongoDB一些常见及生僻的问题做出分析：</p>
<h2 id="32位-vs-64位"><a href="#32位-vs-64位" class="headerlink" title="32位 vs 64位"></a>32位 vs 64位</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;现在大多数的服务器都对32位操作系统实现支持，更有许多新型硬件支持着允许更多RAM的64位操作系统。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;MongoDB也同时发布了32位及64位两个版本的数据库。归结于MongoDB使用的内存映射文件，32位版本只支持2G数据的存储。对于标准的Replica Set，MongoDB只拥有单一的处理策略 —— mongod。如果你想在未来储存2G以上的数据，请使用64位版本的MongoDB。如果拥有分片安装，那么32位版本同样可以使用。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;总结：使用64位版本或者理解32位版本的限制。</p>
<h2 id="文件大小限制"><a href="#文件大小限制" class="headerlink" title="文件大小限制"></a>文件大小限制</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;不同于RDBMS把数据储存在行与列中，MongoDB的数据是储存在文件中的。这些文件使用二进制存储形式，其格式为类似JSON格式的BSON格式。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;和其它的数据库一样，单个文件的储存大小是有限制的。在旧版本的MongoDB中，单个文件都限制在4M以内。而新版本的MongoDB单文件已经支持到16M大小。这样的限制也许是令人厌烦的，但是10gen的意见是：如果这项设置不停的困扰到你，那么是否你的设计模式存在着问题;或者你可以使用文件无大小限制的GridFS。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;这种情况通常的建议是避免存储过大的文件，不定期的更新数据库中存储的各种对象。而像Amazon S3或者Rackspace Cloudfiles这样的服务通常可能会是更好的选择，而非必要情况下最好别让基础设施陷入过载。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;总结：把每个文件保持在16M以下，那么一切都好。</p>
<h2 id="写入失败"><a href="#写入失败" class="headerlink" title="写入失败"></a>写入失败</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;MongoDB在默认的情况下允许高速的写入和更新，而付出的代价就是没有明确的错误通知。默认情况下多数的驱动都在做异步、“不安全”写入 —— 这就意味着驱动程序不能立即反馈错误信息，类似于MySQL的INSERT DELAYED。如果你想知道某个事情是否成功，你必须使用getLastError手动的检查错误信息。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;某些情况下如果你需要在错误发生后立刻得到错误信息，即：大多数的驱动中都很容易实现同步“安全”查询。这将谋杀掉MongoDB不同于传统数据库的优点。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;如果对比“完全安全”的同步写入你需要多一点性能，同时还想要一定程度的安全，那么你可以使用getLastError with‘j’让MongoDB只到一份日志提交后再发出错误报告通知。那么日志将以100毫秒一次的速度输出到磁盘，而不是60秒。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;总结：如果必须要写入确认，你可以使用安全写入或getLastError。</p>
<h2 id="数据结构模型的弱化不等于没有数据结构模型"><a href="#数据结构模型的弱化不等于没有数据结构模型" class="headerlink" title="数据结构模型的弱化不等于没有数据结构模型"></a>数据结构模型的弱化不等于没有数据结构模型</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;RDBMS一般都拥有一个预定义的数据结构模型：表格的行和列，每个字段都拥有名称和数据类型。如果你想给其中一行加一列，那么你必须给整个表格都添加一列。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;MongoDB则是移除了这个设置，对于Collection和文件没有强制的模型限定。这有益于快速开发及简易修改。<br>　　当然这不意味着你就可以无视结构模型的设计，一个合适的结构模型可以让你获得MongoDB的最佳性能。赶快阅读MongoDB文档，或者观看这些结构模型设计的相关视频吧!</p>
<p>　　Schema Design Basics<br>　　Schema Design at Scale<br>　　Schema Design Principles and Practice</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;总结：设计结构模型并充分利用MongoDB的特色。</p>
<h2 id="默认情况下修改语句修改的只是单个文件"><a href="#默认情况下修改语句修改的只是单个文件" class="headerlink" title="默认情况下修改语句修改的只是单个文件"></a>默认情况下修改语句修改的只是单个文件</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在传统的RDBMS中除非使用LIMIT子句，修改语句作用的将是所有匹配的地方。然而MongoDB每个查询上都默认使用等价“LIMIT 1”的设置。虽然无法做到“LIMIT 5”，但是你可以通过下面的语句整个的移除限制：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">db.people.update(&#123;age: &#123;<span class="variable">$gt</span>: 30&#125;&#125;, &#123;<span class="variable">$set</span>: &#123;past_it: <span class="literal">true</span>&#125;&#125;, <span class="literal">false</span>, <span class="literal">true</span>)</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;同样在官方的驱动中还有类似的选项 —— ‘multi’。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;总结：可以通过指定多个文件的multi为true来完成多文件修改</p>
<h2 id="查询区分大小写"><a href="#查询区分大小写" class="headerlink" title="查询区分大小写"></a>查询区分大小写</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;字符串的查询可能不按预期的那样发展 —— 这归结于MongoDB默认区分大小写。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;例如：db.people.find({name: ‘Russell’})与db.people.find({name: ‘ russell‘})是不同的。在这里最理想的解决方案就是对需要查询数据进行确认。你也可以通过正则表达式进行查询，比如：db.people.find({name:/Russell/i})，但是这样会影响到性能。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;总结：查询是区分大小写的，在牺牲速度的情况下可以利用正则表达式。</p>
<h2 id="对输入的数据无容错性"><a href="#对输入的数据无容错性" class="headerlink" title="对输入的数据无容错性"></a>对输入的数据无容错性</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;当你尝试向传统数据库插入错误类型的数据，传统的数据库一般会把数据转换成预定义的类型。然而这在MongoDB中是行不通的，因为MongoDB的文件是没有预定义数据模型的。这样的话MongoDB会插入你输入的任何数据。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;总结：使用准确的数据类型。</p>
<h2 id="关于锁"><a href="#关于锁" class="headerlink" title="关于锁"></a>关于锁</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;当资源被代码的多个部分所共享时，需要确信锁必须要确保这处资源只能在一个地方被操作。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;旧版本的MongoDB (pre 2.0)拥有一个全局的写入锁。这就意味贯穿整个服务器中只有一个地方做写操作。这就可能导致数据库因为某个地方锁定超负载而停滞。这个问题在2.0版本中的得到了显著的改善，并且在当前2.2版本中得到了进一步的加强。MongoDB 2.2使用数据库级别的锁在这个问题上迈进了一大步。同样值得期待的Collection级别的锁也计划在下一个版本中推出。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;尽管如此，Russell还是认为：大多数受此限制的应用程序于其说是受MongoDB影响，还不如说是程序本身的问题来的更直接。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;总结：使用最新的稳定版本才能获得最高的性能。</p>
<h2 id="关于包"><a href="#关于包" class="headerlink" title="关于包"></a>关于包</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在类Ubuntu和Debian系统上安装时，许多人都出现过“过时版本”这样的问题。解决方案很简单：使用10gen官方库，那么在Ubuntu和Debian上安装也会像在Fedora和Centos上安装一样流畅。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;总结：使用拥有大多数最新版本的官方包。</p>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/MongoDB/10.   Mongodb 3.0 配置身份验证db.createUser()说明/">Mongodb 3.0 配置身份验证db.createUser()说明</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/MongoDB/">MongoDB</a>
			</span>
		
	</div>

	

	
		<h2 id="定义："><a href="#定义：" class="headerlink" title="定义："></a>定义：</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;创建一个数据库新用户用db.createUser()方法，如果用户存在则返回一个用户重复错误。</p>
<h2 id="语法："><a href="#语法：" class="headerlink" title="语法："></a>语法：</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">db.createUser(user, writeConcern)</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;user这个文档创建关于用户的身份认证和访问信息；</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;writeConcern这个文档描述保证MongoDB提供写操作的成功报告。</p>
<h3 id="user文档，定义了用户的以下形式-："><a href="#user文档，定义了用户的以下形式-：" class="headerlink" title="user文档，定义了用户的以下形式**："></a>user文档，定义了用户的以下形式**：</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">&#123; user: <span class="string">"&lt;name&gt;"</span>,</div><div class="line">  <span class="built_in">pwd</span>: <span class="string">"&lt;cleartext password&gt;"</span>,</div><div class="line">  customData: &#123; &lt;any information&gt; &#125;,</div><div class="line">  roles: [</div><div class="line">    &#123; role: <span class="string">"&lt;role&gt;"</span>, db: <span class="string">"&lt;database&gt;"</span> &#125; | <span class="string">"&lt;role&gt;"</span>,</div><div class="line">    ...</div><div class="line">  ]</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h3 id="user文档字段介绍："><a href="#user文档字段介绍：" class="headerlink" title="user文档字段介绍："></a>user文档字段介绍：</h3><ul>
<li>user字段，为新用户的名字；</li>
<li>pwd字段，用户的密码；</li>
<li>cusomData字段，为任意内容，例如可以为用户全名介绍；</li>
<li>roles字段，指定用户的角色，可以用一个空数组给新用户设定空角色；</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在roles字段,可以指定内置角色和用户定义的角色。</p>
<h3 id="Built-In-Roles（内置角色）："><a href="#Built-In-Roles（内置角色）：" class="headerlink" title="Built-In Roles（内置角色）："></a>Built-In Roles（内置角色）：</h3><ol>
<li><p>数据库用户角色：read、readWrite;</p>
</li>
<li><p>数据库管理角色：dbAdmin、dbOwner、userAdmin；</p>
</li>
<li><p>集群管理角色：clusterAdmin、clusterManager、clusterMonitor、hostManager；</p>
</li>
<li><p>备份恢复角色：backup、restore；</p>
</li>
<li><p>所有数据库角色：readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase</p>
</li>
<li><p>超级用户角色：root  </p>
</li>
</ol>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>这里还有几个角色间接或直接提供了系统超级用户的访问（dbOwner 、userAdmin、userAdminAnyDatabase）</strong></p>
<ol>
<li>内部角色：__system</li>
</ol>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>PS：关于每个角色所拥有的操作权限可以点击上面的内置角色链接查看详情</strong>。</p>
<h3 id="writeConcern文档（官方说明）"><a href="#writeConcern文档（官方说明）" class="headerlink" title="writeConcern文档（官方说明）"></a>writeConcern文档（官方说明）</h3><ul>
<li>w选项：允许的值分别是 1、0、大于1的值、”majority”、<tag set="">；</tag></li>
<li>j选项：确保mongod实例写数据到磁盘上的journal（日志），这可以确保mongd以外关闭不会丢失数据。设置true启用。</li>
<li>wtimeout：指定一个时间限制,以毫秒为单位。wtimeout只适用于w值大于1。</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;例如：在products数据库创建用户accountAdmin01，并给该用户admin数据库上clusterAdmin和readAnyDatabase的角色，products数据库上readWrite角色。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">use products</div><div class="line">db.createUser( &#123; <span class="string">"user"</span> : <span class="string">"accountAdmin01"</span>,</div><div class="line">                 <span class="string">"pwd"</span>: <span class="string">"cleartext password"</span>,</div><div class="line">                 <span class="string">"customData"</span> : &#123; employeeId: 12345 &#125;,</div><div class="line">                 <span class="string">"roles"</span> : [ &#123; role: <span class="string">"clusterAdmin"</span>, db: <span class="string">"admin"</span> &#125;,</div><div class="line">                             &#123; role: <span class="string">"readAnyDatabase"</span>, db: <span class="string">"admin"</span> &#125;,</div><div class="line">                             <span class="string">"readWrite"</span></div><div class="line">                             ] &#125;,</div><div class="line">               &#123; w: <span class="string">"majority"</span> , wtimeout: 5000 &#125; )</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;验证：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">mongo -u accountAdmin01 -p yourpassward --authenticationDatabase products</div></pre></td></tr></table></figure>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/MongoDB/1. MongoDB 介绍/">MongoDB 介绍</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/MongoDB/">MongoDB</a>
			</span>
		
	</div>

	

	
		<h2 id="什么是-MongoDB"><a href="#什么是-MongoDB" class="headerlink" title="什么是 MongoDB"></a>什么是 MongoDB</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;MongoDB 是由C++语言编写的，是一个基于分布式文件存储的开源数据库系统，属于NoSQL。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在高负载的情况下，可以添加更多的节点，可以保证服务器性能。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;MongoDB 旨在为WEB应用提供可扩展的高性能数据存储解决方案。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;MongoDB 将数据存储为一个文档，数据结构由键值(key=&gt;value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档，数组及文档数组。</p>
<h2 id="和关型数据库对比"><a href="#和关型数据库对比" class="headerlink" title="和关型数据库对比"></a>和关型数据库对比</h2><p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/MongoDB%20%E4%BB%8B%E7%BB%8D/01.png?raw=true" alt=""></figure></p>
<h2 id="关系型数据库数据结构"><a href="#关系型数据库数据结构" class="headerlink" title="关系型数据库数据结构"></a>关系型数据库数据结构</h2><p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/MongoDB%20%E4%BB%8B%E7%BB%8D/02.png?raw=true" alt=""></figure></p>
<h2 id="MongoDB-数据结构"><a href="#MongoDB-数据结构" class="headerlink" title="MongoDB 数据结构"></a>MongoDB 数据结构</h2><p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/MongoDB%20%E4%BB%8B%E7%BB%8D/03.png?raw=true" alt=""></figure></p>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/Memcached/4. Memcached数据被踢(evictions0)现象分析/">Memcached数据被踢(evictions0)现象分析</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/Memcached/">Memcached</a>
			</span>
		
	</div>

	

	
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;很多同学可能熟知Memcached的LRU淘汰算法，它是在slab内部进行的，如果所有空间都被slabs分配，即使另外一个slab里面有空位，仍然存在踢数据可能。你可以把slab理解为教室，如果你的教室满了，即使别的教室有空位你的教室也只能踢人才能进人。</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Memcached%E6%95%B0%E6%8D%AE%E8%A2%AB%E8%B8%A2evictions0%E7%8E%B0%E8%B1%A1%E5%88%86%E6%9E%90/01.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;本文介绍的却是另外一种现象。今天监控发现线上一memcached发生数据被踢现象，用stats命令看evictions&gt;0,因为以前也出现过此问题，后来对这个参数增加了一个监控，所以这次主动就发现了。由于给memcached分配的内存远大于业务存储数据所需内存，因此初步判断是“灵异现象”。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;第一步，netstat查看所有连接，排除是否被一些未规划的client使用，经排查后断定无此可能。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;第二步，用tcpdump抽样检查set的指令，排除是否有忘记设cache过期时间的client，初步检查所有典型的业务都有expire time。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;第三步，Google，未果</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;第四步，看源代码，了解evictions计数器增加时的具体细节.</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;in items.c, memcached-1.2.8,</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">for</span> (search = tails[id]; tries &gt; 0 &amp;&amp; search != NULL; tries--, search=search-&gt;prev) &#123;</div><div class="line">    <span class="keyword">if</span> (search-&gt;refcount == 0) &#123;</div><div class="line">        <span class="keyword">if</span> (search-&gt;exptime == 0 || search-&gt;exptime &gt; current_time) &#123;</div><div class="line">            itemstats[id].evicted++;</div><div class="line">            itemstats[id].evicted_time = current_time - search-&gt;time;</div><div class="line">            STATS_LOCK();</div><div class="line">            stats.evictions++;</div><div class="line">            STATS_UNLOCK();</div><div class="line">        &#125;</div><div class="line">        do_item_unlink(search);</div><div class="line">        <span class="built_in">break</span>;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;从源代码发现踢数据只判断一个条件，if (search-&gt;refcount == 0)，这个refcount是多线程版本计数用，在当前服务器未启用多线程情况下，refcount应该始终为0,因此初步判断memcached是从访问队列尾部直接踢数据。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;为了证实想法，设计以下场景：</p>
<ol>
<li><p>部署一个memcached测试环境，分配比较小的内存，比如8M</p>
</li>
<li><p>设置1条永远不过期的数据到memcached中，然后再get一次，这条数据后续应该存在LRU队尾。</p>
</li>
<li><p>每隔1S向memcached set(并get一次) 1,000条数据，过期时间设为3秒。</p>
</li>
<li><p>一段时间后，stats命令显示evictions=1</p>
</li>
</ol>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;按我以前的理解，第2步的数据是永远不会被踢的，因为有足够过期的数据空间可以给新来的数据用，LRU淘汰算法应该跳过没过期的数据，但结果证实这种判断是错误的。以上业务的服务器发生被踢的现象是由于保存了大量存活期短的key/value,且key是不重复的。另外又有一业务保存了小量不过期的数据，因此导致不过期的数据惨遭被挤到队列踢出。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;本来这个问题就告一段落了，但在写完这篇文章后，顺便又看了新一代memcached 1.4.1的源代码，很惊喜发现以下代码被增加。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;items.c, memcached 1.4.1<br>/<em> do a quick check if we have any expired items in the tail.. </em>/<br>int tries = 50;<br>item *search;</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">for</span> (search = tails[id];</div><div class="line">    tries &gt; 0 &amp;&amp; search != NULL;</div><div class="line">    tries--, search=search-&gt;prev) &#123;</div><div class="line">    <span class="keyword">if</span> (search-&gt;refcount == 0 &amp;&amp;</div><div class="line">        (search-&gt;exptime != 0 &amp;&amp; search-&gt;exptime &lt; current_time)) &#123;</div><div class="line">        it = search;</div><div class="line">        /* I don<span class="string">'t want to actually free the object, just steal</span></div><div class="line"><span class="string">         * the item to avoid to grab the slab mutex twice ;-)</span></div><div class="line"><span class="string">         */</span></div><div class="line"><span class="string">        it-&gt;refcount = 1;</span></div><div class="line"><span class="string">        do_item_unlink(it);</span></div><div class="line"><span class="string">        /* Initialize the item block: */</span></div><div class="line"><span class="string">        it-&gt;slabs_clsid = 0;</span></div><div class="line"><span class="string">        it-&gt;refcount = 0;</span></div><div class="line"><span class="string">        break;</span></div><div class="line"><span class="string">    &#125;</span></div><div class="line"><span class="string">&#125;</span></div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;重复进行上述测试，未发生evictions。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;9/8 Update: 注意到L108的tries=50没有？试想把测试第2步设置51条不过期数据到cache中，情况会怎样？因此新版的Memcached也同样存在本文描述问题。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;几条总结：</p>
<ul>
<li>过期的数据如果没被显式调用get，则也要占用空间。</li>
<li>过期的不要和不过期的数据存在一起，否则不过期的可能被踢。</li>
<li>从节约内存的角度考虑，即使数据会过期，也不要轻易使用随机字符串作为key，尽量使用定值如uid，这样占用空间的大小相对固定。</li>
<li>估算空间大小时候请用slab size计算，不要按value长度去计算。</li>
<li>不要把cache当作更快的key value store来用, cache不是storage。</li>
</ul>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/Memcached/3. Memcache 查看列出所有key方法/">Memcache 查看列出所有key方法</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/Memcached/">Memcached</a>
			</span>
		
	</div>

	

	
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;今天在做一个Memcache的session测试，但是在测试的过程中，发现Memcache没有一个比较简单的方法可以直接象redis那样keys *列出所有的Session key，并根据key get对应 的session内容</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;于是，我开始查找资料，翻出来的大部分是一些memcache常用命令等</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;但是对列出key的办法，讲解却不多，于是来到google，找到了一个国外的资料</p>
<p><a href="http://www.darkcoding.net/software/memcached-list-all-keys/" target="_blank" rel="external">http://www.darkcoding.net/software/memcached-list-all-keys/</a></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;cmd上登录memcache</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;telnet 127.0.0.1 11211</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;具体的内容我套用我的测试环境中，操作如下</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;首先列出所有keys</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">stats items</div><div class="line">STAT items:7:number 1</div><div class="line">STAT items:7:age 188</div><div class="line">END</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;接下来基于列出的items id，本例中为7，第2个参数为列出的长度，0为全部列出</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">stats cachedump 7 0</div><div class="line">ITEM Sess_sidsvpc1473t1np08qnkvhf6j2 [183 b; 1394527347 s]</div><div class="line">END</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;上面的stats cachedump命令列出了我的session key，接下来就用get命令查找对应的 session值</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">get Sess_sidsvpc1473t1np08qnkvhf6j2</div><div class="line">VALUE</div><div class="line">Sess_sidsvpc1473t1np08qnkvhf6j2 1440 1</div><div class="line">83</div><div class="line">Sess_|a:5:&#123;s:6:”verify”;s:32:”e70981fd305170c41a5632b2a24bbcaa”;s:3:”uid”;s:1:”1</div><div class="line">“;s:8:”username”;s:5:”admin”;s:9:”logintime”;s:19:”2014-03-11 16:24:25″;s:7:”<span class="built_in">log</span></div><div class="line">inip”;s:9:”127.0.0.1″;&#125;</div></pre></td></tr></table></figure>
	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/Memcached/2. memcached安装/">memcached安装</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/Memcached/">Memcached</a>
			</span>
		
	</div>

	

	
		<h2 id="1-memcached安装"><a href="#1-memcached安装" class="headerlink" title="1.memcached安装"></a>1.memcached安装</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@memcached ~]<span class="comment"># yum install -y epel-release</span></div></pre></td></tr></table></figure>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@memcached ~]<span class="comment"># yum install -y libevent memcached libmemcached</span></div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;启动 /etc/init.d/memcached start</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">[root@memcached ~]<span class="comment"># service memcached start</span></div><div class="line">正在启动 memcached： [确定]</div></pre></td></tr></table></figure>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@memcached ~]<span class="comment"># ps aux |grep memcached</span></div></pre></td></tr></table></figure>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/memcached%E5%AE%89%E8%A3%85/01.png?raw=true" alt="01"><figcaption class="figure__caption">01</figcaption></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;memcached -d -p 11211 -u memcached -m 64 -c 1024 -P /var/run/memcached/memcached.pid</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;相关的参数在/etc/init.d/memcached和/etc/sysconfig/memcached中定义</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@memcached ~]<span class="comment"># vim /etc/sysconfig/memcached</span></div></pre></td></tr></table></figure>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/memcached%E5%AE%89%E8%A3%85/02.png?raw=true" alt="02"><figcaption class="figure__caption">02</figcaption></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;修改为</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/memcached%E5%AE%89%E8%A3%85/03.png?raw=true" alt="03"><figcaption class="figure__caption">03</figcaption></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;将内存改为 128m</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;重启服务</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">[root@memcached ~]<span class="comment"># /etc/init.d/memcached restart</span></div><div class="line">停止 memcached：     [确定]</div><div class="line">正在启动 memcached：  [确定]</div></pre></td></tr></table></figure>
<h3 id="memcached-启动参数"><a href="#memcached-启动参数" class="headerlink" title="memcached 启动参数"></a>memcached 启动参数</h3><ul>
<li>-d选项是启动一个守护进程</li>
<li>-m是分配给Memcache使用的内存数量，单位是MB，这里是200MB </li>
<li>-u是运行Memcache的用户，如果当前为 root 的话，需要使用此参数指定用户。 </li>
<li>-l是监听的服务器IP地址</li>
<li>-p是设置Memcache监听的端口，默认是11211</li>
<li>-c选项是最大运行的并发连接数，默认是1024</li>
<li>-P是设置保存Memcache的pid文件 </li>
</ul>
<h3 id="查看-memcached-运行状态"><a href="#查看-memcached-运行状态" class="headerlink" title="查看 memcached 运行状态"></a>查看 memcached 运行状态</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@memcached ~]<span class="comment"># memcached-tool 127.0.0.1:11211 stats</span></div></pre></td></tr></table></figure>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/memcached%E5%AE%89%E8%A3%85/04.png?raw=true" alt="04"><figcaption class="figure__caption">04</figcaption></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;或者</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@memcached ~]<span class="comment"># echo stats | nc 127.0.0.1:11211</span></div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;需要安装nc工具</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@memcached ~]<span class="comment"># yum install -y nc</span></div></pre></td></tr></table></figure>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/memcached%E5%AE%89%E8%A3%85/05.png?raw=true" alt="05"><figcaption class="figure__caption">05</figcaption></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;若安装libmemcached后，可以使用命令 </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;memstat –servers=127.0.0.1:11211 查看memcached服务状态</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@memcached ~]<span class="comment"># memstat --servers=127.0.0.1:11211</span></div></pre></td></tr></table></figure>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/memcached%E5%AE%89%E8%A3%85/06.png?raw=true" alt="06"><figcaption class="figure__caption">06</figcaption></figure></p>
<h2 id="2-memcached-基本命令"><a href="#2-memcached-基本命令" class="headerlink" title="2.memcached 基本命令"></a>2.memcached 基本命令</h2><h3 id="memcached-命令行"><a href="#memcached-命令行" class="headerlink" title="memcached 命令行"></a>memcached 命令行</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@memcached ~]<span class="comment"># telnet 127.0.0.1 11211</span></div></pre></td></tr></table></figure>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/memcached%E5%AE%89%E8%A3%85/07.png?raw=true" alt="07"><figcaption class="figure__caption">07</figcaption></figure></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/memcached%E5%AE%89%E8%A3%85/08.png?raw=true" alt="08"><figcaption class="figure__caption">08</figcaption></figure></p>
<h3 id="mamcached-语法规则"><a href="#mamcached-语法规则" class="headerlink" title="mamcached 语法规则"></a>mamcached 语法规则</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">&lt;<span class="built_in">command</span> name&gt; &lt;key&gt; &lt;flags&gt; &lt;exptime&gt; &lt;bytes&gt;\r\n &lt;data block&gt;\r\n</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;注：\r\n在windows下是Enter键</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;a) <command name=""> 可以是”set”, “add”, “replace”。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;“set”表示按照相应的<key>存储该数据，没有的时候增加，有的覆盖。</key></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; “add”表示按照相应的<key>添加该数据,但是如果该<key>已经存在则会操作失败。</key></key></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; “replace”表示按照相应的<key>替换数据,但是如果该<key>不存在则操作失败。</key></key></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;b) <key> 客户端需要保存数据的key。</key></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;c) <flags> 是一个16位的无符号的整数(以十进制的方式表示)。</flags></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;该标志将和需要存储的数据一起存储,并在客户端get数据时返回。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;客户可以将此标志用做特殊用途，此标志对服务器来说是不透明的。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;d) <exptime> 过期的时间。</exptime></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;若为0表示存储的数据永远不过期(但可被服务器算法：LRU 等替换)。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;如果非0(unix时间或者距离此时的秒数),当过期后,服务器可以保证用户得不到该数据(以服务器时间为标准)。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;e) <bytes> 需要存储的字节数，当用户希望存储空数据时<bytes>可以为0</bytes></bytes></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;f) <data block="">需要存储的内容，输入完成后，最后客户端需要加上”\r\n”（直接点击Enter）作为”命令头”的结束标志。</data></p>
<h3 id="memcached-数据示例"><a href="#memcached-数据示例" class="headerlink" title="memcached 数据示例"></a>memcached 数据示例</h3><p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/memcached%E5%AE%89%E8%A3%85/09.png?raw=true" alt="09"><figcaption class="figure__caption">09</figcaption></figure></p>
<h2 id="3-PHP-连接-memcached"><a href="#3-PHP-连接-memcached" class="headerlink" title="3.PHP 连接 memcached"></a>3.PHP 连接 memcached</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;先安装php的memcache扩展</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@memcached ~]<span class="comment"># wget http://www.lishiming.net/data/attachment/forum/memcache-2.2.3.tgz</span></div></pre></td></tr></table></figure>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">[root@memcached ~]<span class="comment"># tar zxf memcache-2.2.3.tgz </span></div><div class="line">[root@memcached ~]<span class="comment"># cd memcache-2.2.3</span></div></pre></td></tr></table></figure>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@memcached memcache-2.2.3]<span class="comment"># /usr/local/php/bin/phpize</span></div></pre></td></tr></table></figure>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/memcached%E5%AE%89%E8%A3%85/10.png?raw=true" alt="10"><figcaption class="figure__caption">10</figcaption></figure></p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@memcached memcache-2.2.3]<span class="comment"># ./configure --with-php-config=/usr/local/php/bin/php-config</span></div></pre></td></tr></table></figure>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@memcached memcache-2.2.3]<span class="comment"># make &amp;&amp; make install</span></div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;安装完后会有类似这样的提示：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Installing shared extensions: /usr/<span class="built_in">local</span>/php/lib/php/extensions/no-debug-non-zts-20131226/</div></pre></td></tr></table></figure>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/memcached%E5%AE%89%E8%A3%85/11.png?raw=true" alt="11"><figcaption class="figure__caption">11</figcaption></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;把这个记住，然后修改php.ini</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@memcached memcache-2.2.3]<span class="comment"># vim /usr/local/php/etc/php.ini</span></div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;把 extension_dir = “./“ 修改为<br>extension_dir = “/usr/local/php/lib/php/extensions/no-debug-non-zts-20131226/“</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/memcached%E5%AE%89%E8%A3%85/12.png?raw=true" alt="12"><figcaption class="figure__caption">12</figcaption></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;修改为</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/memcached%E5%AE%89%E8%A3%85/13.png?raw=true" alt="13"><figcaption class="figure__caption">13</figcaption></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;并添加一行</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">extension = <span class="string">"memcache.so"</span></div></pre></td></tr></table></figure>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/memcached%E5%AE%89%E8%A3%85/14.png?raw=true" alt="14"><figcaption class="figure__caption">14</figcaption></figure></p>
<h3 id="PHP-连接-memcached-检验"><a href="#PHP-连接-memcached-检验" class="headerlink" title="PHP 连接 memcached 检验"></a>PHP 连接 memcached 检验</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;检查模块是否添加正常</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@memcached memcache-2.2.3]<span class="comment"># /usr/local/php/bin/php -m | grep memcache</span></div></pre></td></tr></table></figure>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/memcached%E5%AE%89%E8%A3%85/15.png?raw=true" alt="15"><figcaption class="figure__caption">15</figcaption></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;模块中有 memcache 说明配置成功</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;下载测试php脚本</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@memcached memcache-2.2.3]<span class="comment"># curl www.apelearn.com/study_v2/.memcache.txt &gt; 1.php 2&gt;/dev/null</span></div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;执行脚本</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@memcached memcache-2.2.3]<span class="comment"># /usr/local/php/bin/php 1.php</span></div></pre></td></tr></table></figure>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/memcached%E5%AE%89%E8%A3%85/16.png?raw=true" alt="16"><figcaption class="figure__caption">16</figcaption></figure></p>
<h2 id="4-memcached-实现-session-共享"><a href="#4-memcached-实现-session-共享" class="headerlink" title="4.memcached 实现 session 共享"></a>4.memcached 实现 session 共享</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;本实例是在lamp/lnmp环境下实现</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;编辑php.ini添加两行</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@memcached ~]<span class="comment"># vim /usr/local/php/etc/php.ini</span></div></pre></td></tr></table></figure>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">session.save_handler = memcache </div><div class="line">session.save_path = <span class="string">"tcp://127.0.0.1:11211"</span></div></pre></td></tr></table></figure>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/memcached%E5%AE%89%E8%A3%85/17.png?raw=true" alt="17"><figcaption class="figure__caption">17</figcaption></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;如果 session 服务器在远程这里输入远程的 IP 地址</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;或者httpd.conf中对应的虚拟主机中添加</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@memcached ~]<span class="comment"># vim /usr/local/apache2/conf/httpd.conf</span></div></pre></td></tr></table></figure>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">php_value session.save_handler <span class="string">"memcache"</span> </div><div class="line">php_value session.save_path <span class="string">"tcp://192.168.0.9:11211"</span></div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 或者php-fpm.conf对应的pool中添加</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">php_value[session.save_handler] = memcache</div><div class="line">php_value[session.save_path] = <span class="string">" tcp://192.168.0.9:11211 "</span></div></pre></td></tr></table></figure>
<h3 id="memcached-session测试"><a href="#memcached-session测试" class="headerlink" title="memcached session测试"></a>memcached session测试</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@memcached ~]<span class="comment"># wget http://study.lishiming.net/.mem_se.txt</span></div></pre></td></tr></table></figure>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@memcached ~]<span class="comment"># mv .mem_se.txt /usr/local/apache2/htdocs/session.php</span></div></pre></td></tr></table></figure>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@memcached ~]<span class="comment"># curl localhost/session.php</span></div></pre></td></tr></table></figure>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/memcached%E5%AE%89%E8%A3%85/18.png?raw=true" alt="18"><figcaption class="figure__caption">18</figcaption></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;类似于1443702394<br><br>1443702394<br><br>i44nunao0g3o7vf2su0hnc5440</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@memcached ~]<span class="comment"># telnet 127.0.0.1 11211</span></div></pre></td></tr></table></figure>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/memcached%E5%AE%89%E8%A3%85/19.png?raw=true" alt="19"><figcaption class="figure__caption">19</figcaption></figure></p>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/Memcached/1. Memcached/">Memcached</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/Memcached/">Memcached</a>
			</span>
		
	</div>

	

	
		<h2 id="1-memcached介绍"><a href="#1-memcached介绍" class="headerlink" title="1.memcached介绍"></a>1.memcached介绍</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;是国外社区网站LiveJournal团队开发，通过缓存数据库查询结果，减少数据库访问次数，从而提高动态web站点性能。<a href="http://memcached.org/" target="_blank" rel="external">官方站点</a></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;基于c/s架构，协议简单</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;基于libevent的事件处理</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;自主内存存储处理（slab allowcation)</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;数据过期方式：Lazy Expiration 和 LRU</p>
<h2 id="2-memcached数据流"><a href="#2-memcached数据流" class="headerlink" title="2.memcached数据流"></a>2.memcached数据流</h2><p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/memcached/01.png?raw=true" alt=""></figure></p>
<h2 id="3-slab-allocation"><a href="#3-slab-allocation" class="headerlink" title="3.slab allocation"></a>3.slab allocation</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Slab Allocation的原理——将分配的内存分割成各种尺寸的块（chunk）， 并把尺寸相同的块分成组（chunk的集合），每个chunk集合被称为slab。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Memcached的内存分配以Page为单位，Page默认值为1M，可以在启动时通过-I参数来指定。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Slab是由多个Page组成的，Page按照指定大小切割成多个chunk。其结构如图</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/memcached/02.png?raw=true" alt=""></figure></p>
<h2 id="4-Growth-factor"><a href="#4-Growth-factor" class="headerlink" title="4.Growth factor"></a>4.Growth factor</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;memcached在启动时通过-f选项可以指定 Growth Factor因子。该值控制chunk大小的差异。默认值为1.25。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;通过memcached-tool查看指定memcached实例的不同slab状态，可以看到各Item所占大小（chunk大小）差距为1.25</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;命令：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">memcached-tool 127.0.0.1:11211 display</div></pre></td></tr></table></figure>
<h2 id="5-memcached数据过期方式"><a href="#5-memcached数据过期方式" class="headerlink" title="5.memcached数据过期方式"></a>5.memcached数据过期方式</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Lazy Expiration </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;memcached 内部不会监视记录是否过期，而是在get时查看记录的时间戳，检查记录是否过期。这种技术被称为lazy（惰性）expiration。因此，memcached不会在过期监视上耗费CPU时间。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;LRU</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;memcached会优先使用已超时的记录的空间，但即使如此，也会发生追加新记录时空间不足的情况，此时就要使用名为Least Recently Used（LRU）机制来分配空间。顾名思义，这是删除“最近最少使用”的记录的机制。因此，当内存空间不足时（无法从slab class获取到新的空间时），就从最近未被使用的记录中搜索，并将其空间分配给新的记录。从缓存的实用角度来看，该模型十分理想。</p>

	

	

</article>





	<span class="different-posts">📖 <a href="/page/33">more posts</a> 📖</span>



	</main>

	<footer class="footer">
	<div class="footer-content">
		
	      <div class="footer__element">
	<p>Hi there, <br />welcome to my Blog glad you found it. Have a look around, will you?</p>
</div>

	    
	      <div class="footer__element">
	<h5>Check out</h5>
	<ul class="footer-links">
		<li class="footer-links__link"><a href="/archives">Archive</a></li>
		
		  <li class="footer-links__link"><a href="/atom.xml">RSS</a></li>
	    
		<li class="footer-links__link"><a href="/about">about page</a></li>
		<li class="footer-links__link"><a href="/tags">Tags</a></li>
		<li class="footer-links__link"><a href="/categories">Categories</a></li>
	</ul>
</div>

	    

		<div class="footer-credit">
			<span>© 2017 失落的乐章 | Powered by <a href="https://hexo.io/">Hexo</a> | Theme <a href="https://github.com/HoverBaum/meilidu-hexo">MeiliDu</a></span>
		</div>

	</div>


</footer>



</body>

</html>
